package com.linkgie.galaxyframework.utils.concurrent;

import java.util.concurrent.Callable;

import com.linkgie.galaxyframework.utils.ExecutionContext;
import com.linkgie.galaxyframework.utils.ExecutionContextHandle;
import com.linkgie.galaxyframework.utils.ExecutionScheduler;
import com.linkgie.galaxyframework.utils.RethrowException;

public abstract class ThreadUtils {

	/**
	 * 以不可中断的方式使当前线程 sleep 指定时长；
	 * <p>
	 * 
	 * 
	 * 
	 * @param millis 要等待的时长；单位毫秒；
	 */
	public static void sleepUninterrupted(long millis) {
		if (millis < 0) {
			throw new IllegalArgumentException("The 'millis' arg is negative!");
		}
		long start = System.currentTimeMillis();
		// 逝去时间；
		long elapseTime = 0;
		while (elapseTime < millis) {
			// 当剩余时间大于 10 秒时，将休眠间隔减半，以便提高休眠精度；
			long leftTime = millis - elapseTime;
			long interval = leftTime > 10000 ? leftTime >> 2 : leftTime;
			try {
				Thread.sleep(interval);
			} catch (InterruptedException e) {
				// ignore ;
			}
			long now = System.currentTimeMillis();
			long temp = now - start;
			elapseTime += (temp > 0 ? temp : 0);// 防止长时间等待时，由于系统时钟的变化而出现计时倒转；
			start = now;
		}
	}

	public static void sleep(long millis) {
		if (millis < 0) {
			throw new IllegalArgumentException("The 'millis' arg is negative!");
		}
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			// swallow interrupted exception;
		}
	}

	public static void switchContext(ClassLoader contextClassLoader, Runnable runnable) {
		ExecutionContext context = createThreadContext(contextClassLoader);
		context.run(runnable);
	}

	public static <T> T switchContext(ClassLoader contextClassLoader, Callable<T> callable) {
		ExecutionContext context = createThreadContext(contextClassLoader);
		return context.call(callable);
	}

	public static ExecutionContext createThreadContext(ClassLoader contextClassLoader) {
		return ExecutionScheduler.buildContext(new ExecutionContextHandle<ClassLoader>() {

			@Override
			public ClassLoader init() {
				ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
				Thread.currentThread().setContextClassLoader(contextClassLoader);
				return origClassLoader;
			}

			@Override
			public void clean(ClassLoader origClassLoader) {
				Thread.currentThread().setContextClassLoader(origClassLoader);
			}

		});
	}

}
